package com.xshang.SqlSession;

import com.xshang.executor.Executor;
import com.xshang.pojo.Configuration;
import com.xshang.pojo.MappedStatement;

import java.beans.IntrospectionException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.sql.SQLException;
import java.util.Collections;
import java.util.List;

public class DefaultSqlSession implements SqlSession {

    private Configuration configuration;

    private Executor executor;

    public DefaultSqlSession(Configuration configuration, Executor executor) {
        this.configuration = configuration;
        this.executor = executor;
    }

    @Override
    public <E> List<E> selectList(String statementId, Object param) throws SQLException, IntrospectionException, NoSuchFieldException, ClassNotFoundException, InvocationTargetException, IllegalAccessException, InstantiationException {
        // 将查询操作委派给底层的执行器
        // query() 执行底层JDBC
        // 数据库配置信息， sql配置信息 都在configuration中
        MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
        return executor.query(configuration, mappedStatement, param);
    }

    @Override
    public <T> T selectOne(String statementId, Object param) throws SQLException, IntrospectionException, NoSuchFieldException, ClassNotFoundException, InvocationTargetException, IllegalAccessException, InstantiationException {
        List<Object> objects = this.selectList(statementId, param);
        if (objects.size() == 1) {
            return (T) objects.get(0);
        } else if (objects.size() > 1) {
            throw new RuntimeException("Expect a result, but the actual number of returned results is greater than one");
        }
        return (T) Collections.emptyList();
    }

    @Override
    public void close() {
        executor.close();
    }

    @Override
    public <T> T getMapper(Class<?> mapperClass) {
        // jdk动态代理生成基于接口的代理对象
        Object proxyInstance = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {
            /*
                Object proxy ： 代理对象的引用
                Method method： 被调用的方法的字节码对象
                Object[] args： 被调用方法的方法入参
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 逻辑： 执行底层的JDBC方法
                // 通过调用sqlSession里面的方法来完成方法调用
                // 参数准备：statementId -- com.xshang.dao.IUserDao
                //          param
                //  问题1  无法获取现有的statementId

                // 方法名  findAll
                String methodName = method.getName();
                // 声明类
                String className = method.getDeclaringClass().getName();
                String statementId = className + "." + methodName;

                // 方法调用 问题2 调用sqlSession中的什么方法(增删改)
                // sqlCommandType
                MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
                // select update delete insert
                String sqlCommandType = mappedStatement.getSqlCommandType();

                switch (sqlCommandType) {
                    case "select":
                        // 执行查询方法调用
                        // 该调用查询所有还是查询单个
                        // 返回值类型
                        Type getGenericReturnType = method.getGenericReturnType();
                        // 判断是否实现了泛型类型参数化
                        if (getGenericReturnType instanceof ParameterizedType) {
                            if (args != null) {
                                return selectList(statementId, args[0]);
                            }
                            return selectList(statementId, null);
                        }
                        return selectOne(statementId, args[0]);
                    case "update":
                        // 执行更新方法调用
                        break;
                    case "delete":
                        // 执行删除方法调用
                        break;
                    case "insert":
                        // 执行新增方法调用
                        break;

                }

                return null;
            }
        });
        return (T) proxyInstance;
    }
}
